package com.maochd.cloud.excel.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import com.maochd.cloud.api.file.service.RemoteFileService;
import com.maochd.cloud.common.core.domain.R;
import com.maochd.cloud.common.core.domain.UserInfo;
import com.maochd.cloud.common.core.exception.BaseException;
import com.maochd.cloud.common.core.utils.file.FileUtil;
import com.maochd.cloud.common.core.utils.user.UserUtil;
import com.maochd.cloud.excel.constant.ExcelConstant;
import com.maochd.cloud.excel.dto.ImportDto;
import com.maochd.cloud.excel.entity.ImportRecord;
import com.maochd.cloud.excel.enums.ImportEnum;
import com.maochd.cloud.excel.service.ImportService;
import com.maochd.cloud.excel.utils.ExcelThreadUtil;
import com.maochd.cloud.excel.utils.ExcelUtils;
import com.maochd.cloud.excel.utils.SpringUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutableTriple;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class ImportServiceImpl implements ImportService {

    @Resource
    private RemoteFileService remoteFileService;

    @SneakyThrows
    private void importExcel(InputStream inputStream, Integer type, ImportRecord importRecord) {
        TimeUnit.SECONDS.sleep(1);
        ByteArrayInputStream in = null;
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            // 识别导入类型
            ImportEnum importEnum = ImportEnum.convert(type);
            // 封装导入类
            ImportDto importDto = new ImportDto();
            // 通过反射执行业务方法
            MutableTriple<Class<?>, List<?>, List<?>> mp = SpringUtils.invokeImportDeal(importEnum, inputStream, importDto);
            if (CollUtil.isNotEmpty(mp.getMiddle())) {
                // 处理失败数据
                in = ExcelUtils.generateFailExcel(mp.getMiddle(), os, mp.getLeft());
                // 失败数据文件上传OSS
                String filename = ExcelUtils
                        .fileName(LocalDateTime.now(), importEnum.getName() + ExcelConstant.FAIL_DATA_SUFFIX);
                R<String> uploadResult = remoteFileService.uploadInner(FileUtil.InputStreamToMultipartFile(
                        in, filename), ExcelUtils.generateUploadFilename(ExcelConstant.IMPORT_FAIL_PATH, filename));
                if (R.isNotOk(uploadResult)) {
                    throw new BaseException(uploadResult.getBizMsg());
                }
                importRecord.setFailFileUrl(uploadResult.getData());
                importRecord.setStatus(ExcelConstant.IMPORT_FAIL);
            }
            importRecord.setFailCount(mp.getMiddle().size());
            importRecord.setSuccessCount(mp.getRight().size() - importRecord.getFailCount());
            importRecord.setStatus(ExcelConstant.IMPORT_SUCCESS);
        } catch (Exception e) {
            log.error(ExcelConstant.IMPORT_ADAPTER_ERROR, e);
            e.printStackTrace();
            importRecord.setFailReason(e.getMessage());
            importRecord.setStatus(ExcelConstant.IMPORT_ERROR);
        } finally {
            IoUtil.close(os);
            IoUtil.close(in);
        }
        importRecord.updateById();
    }

    @Override
    @SneakyThrows
    public void importAdapter(MultipartFile file, Integer type) {
        // 读取文件流，异步执行会出现问题，提前读取
        InputStream inputStream = file.getInputStream();
        String fileName = file.getOriginalFilename();
        UserInfo currentUser = UserUtil.userInfo();
        // 生成导入记录
        ImportRecord importRecord = ImportRecord.builder()
                .operator(currentUser.getUsername())
                .userId(currentUser.getId())
                .fileName(fileName)
                .createDate(LocalDateTime.now())
                .importType(type)
                .status(ExcelConstant.IMPORTING)
                .build();
        importRecord.insert();
        CompletableFuture.runAsync(() -> this.importExcel(inputStream, type, importRecord), ExcelThreadUtil.EXECUTOR);
    }
}
